/********************************** (C) COPYRIGHT *******************************
 * File Name          : main.c
 * Author             : WCH
 * Version            : V1.0.0
 * Date               : 2022/01/18
 * Description        : Main program body.
 *********************************************************************************
 * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
 * Attention: This software (modified or not) and binary are used for
 * microcontroller manufactured by Nanjing Qinheng Microelectronics.
 *******************************************************************************/
/*
 *@Note
Web Server example, this program is used to demonstrate the function
of configuring the WCHNET chip through a web browser, the WCHNET chip has a built-in web server,
The web page can implement WCHNET's network parameter configuration and password management.
For details on the selection of engineering chips,
please refer to the "CH32V30x Evaluation Board Manual" under the CH32V307EVT\EVT\PUB folder.
 */
#include "string.h"
#include "eth_driver.h"

u8 MACAddr[6];  // MAC address
u8 IPAddr[4];   // IP address
u8 GWIPAddr[4]; // Gateway IP address
u8 IPMask[4];   // subnet mask

u8 SocketId, SocketIdForListen;
u8 RecvBuffer[RECE_BUF_LEN], HTTPDataBuffer[RECE_BUF_LEN];
u8 SocketRecvBuf[WCHNET_MAX_SOCKET_NUM][RECE_BUF_LEN]; // socket receive buffer
u16 DESPORT, SRCPORT;                                  // port
/*********************************************************************
 * @fn      mStopIfError
 *
 * @brief   check if error.
 *
 * @param   iError - error constants.
 *
 * @return  none
 */
void mStopIfError(u8 iError)
{
    if (iError == WCHNET_ERR_SUCCESS)
        return;
    printf("Error: %02X\r\n", (u16)iError);
}

/*********************************************************************
 * @fn      TIM2_Init
 *
 * @brief   Initializes TIM2.
 *
 * @return  none
 */
void TIM2_Init(void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = {0};

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    TIM_TimeBaseStructure.TIM_Period = (uint16_t)(SystemCoreClock / 1000000);
    TIM_TimeBaseStructure.TIM_Prescaler = WCHNETTIMERPERIOD * 1000 - 1;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

    TIM_Cmd(TIM2, ENABLE);
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    NVIC_EnableIRQ(TIM2_IRQn);
}

/*********************************************************************
 * @fn      WCHNET_HandleGlobalInt
 *
 * @brief   Global Interrupt Handle
 *
 * @return  none
 */
extern void WCHNET_HandleSockInt(u8, u8);
void WCHNET_HandleGlobalInt(void)
{
    u8 intstat;
    u16 i;
    u8 socketint;

    intstat = WCHNET_GetGlobalInt(); // get global interrupt flag
    if (intstat & GINT_STAT_UNREACH) // Unreachable interrupt
    {
        printf("GINT_STAT_UNREACH\r\n");
    }
    if (intstat & GINT_STAT_IP_CONFLI) // IP conflict
    {
        printf("GINT_STAT_IP_CONFLI\r\n");
    }
    if (intstat & GINT_STAT_PHY_CHANGE) // PHY status change
    {
        i = WCHNET_GetPHYStatus();
        if (i & PHY_Linked_Status)
            printf("PHY Link Success\r\n");
    }
    if (intstat & GINT_STAT_SOCKET)
    { // socket related interrupt
        for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++)
        {
            socketint = WCHNET_GetSocketInt((u8)i);
            if (socketint)
                WCHNET_HandleSockInt((u8)i, socketint);
        }
    }
}

/*********************************************************************
 * @fn      GPIOInit
 *
 * @brief   GPIO initialization
 *
 * @return  none
 */
void GPIOInit(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

// add mongoose
#include "mongoose.h"
#include "net.h"
#include "hal.h"

#define BLINK_PERIOD_MS 1000 // LED_PIN blinking period in millis
struct mg_mgr mgr;
// This flash space resides at after the 0-wait 320k area
static char *s_flash_space = (char *)(0x8000000 + 320 * 1024);

bool web_load_settings(void *buf, size_t len)
{
    if (*(uint32_t *)s_flash_space != 0xAABBCCDD)
        return false;
    memcpy(buf, s_flash_space, len);
    return true;
}

bool web_save_settings(void *buf, size_t len)
{
    return mg_flash_write(s_flash_space, buf, len);
}

static void timer_fn(void *arg)
{
    struct mg_tcpip_if *ifp = arg;                        // And show
    const char *names[] = {"down", "up", "req", "ready"}; // network stats
    MG_INFO(("Ethernet: %s, IP: %M, rx:%u, tx:%u, dr:%u, er:%u RAM: %lu/%lu",
             names[ifp->state], mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent,
             ifp->ndrop, ifp->nerr, hal_ram_used(), hal_ram_free()));
    gpio_toggle(LED1_PIN);
    gpio_toggle(LED2_PIN);
}

// https://mongoose.ws/documentation/#2-minute-integration-guide
static void fn(struct mg_connection *c, int ev, void *ev_data)
{
    if (ev == MG_EV_HTTP_MSG)
    {
        struct mg_http_message *hm = (struct mg_http_message *)ev_data;
        mg_http_reply(c, 200, "", "ok %p\r\n", hm);
    }
}

/*********************************************************************
 * @fn      main
 *
 * @brief   Main program
 *
 * @return  none
 */
int main(void)
{

    SystemCoreClockUpdate();
    Delay_Init();
    USART_Printf_Init(115200); // USART initialize
    GPIOInit();

    hal_init();
    mg_mgr_init(&mgr);
    mg_log_set(MG_LL_DEBUG);

    MG_INFO(("Starting, CPU freq %g MHz", (double)SystemCoreClock / 1000000));

    // Print chip RAM/Flash configuration, and set to 64/256
    const char *sizes[] = {"128/192", "96/224", "64/256", "32/288"};
    uint32_t mode = (FLASH->OBR >> 8) & 3U;
    MG_INFO(("RAM/FLASH configuration: %s", sizes[mode]));
    // if (mode != 2) set_ram_size(2);

    extern char _end[], _heap_end[];
    MG_INFO(("Heap size: %lu bytes. RAM: used %lu, free %lu", _heap_end - _end,
             hal_ram_used(), hal_ram_free()));

    // Initialise Mongoose network stack
    struct mg_tcpip_driver_stm32f_data driver_data = {.mdc_cr = 1, .phy_addr = 1};
    struct mg_tcpip_if mif = {.mac = GENERATE_LOCALLY_ADMINISTERED_MAC(),
                              // Uncomment below for static configuration:
                              .ip = mg_htonl(MG_U32(192, 168, 21, 223)),
                              .mask = mg_htonl(MG_U32(255, 255, 254, 0)),
                              .gw = mg_htonl(MG_U32(192, 168, 20, 1)),
                              .driver = &mg_tcpip_driver_stm32f,
                              .driver_data = &driver_data};
    mg_tcpip_init(&mgr, &mif);
    mg_timer_add(&mgr, BLINK_PERIOD_MS, MG_TIMER_REPEAT, timer_fn, &mif);

    MG_INFO(("MAC: %M. Waiting for IP...", mg_print_mac, mif.mac));
    while (mif.state != MG_TCPIP_STATE_READY)
    {
        mg_mgr_poll(&mgr, 0);
    }

    MG_INFO(("Initialising application..."));
    mg_http_listen(&mgr, "http://192.168.21.223:80", fn, NULL);
    web_init(&mgr);

    MG_INFO(("Starting event loop"));
    for (;;)
    {
        mg_mgr_poll(&mgr, 0);
    }

    return 0;
}
